package com.xq.aspect;

import com.xq.annotation.RateLimit;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
@Aspect
@Log4j2
public class RateLimitAspect {

    @Autowired
    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.xq.annotation.RateLimit)")
    public void rateLimit() {
    }

    @Before("rateLimit()")
    public void acquire(JoinPoint jp) {
        acquire0(jp);
    }

    private void acquire0(JoinPoint jp) {
        Signature signature = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        Object[] args = jp.getArgs();
        String[] parameters = methodSignature.getParameterNames();
        Map<String, Object> parameterMap = new HashMap<>();
        if (parameters != null) {
            for (int i = 0; i < parameters.length; i++) {
                parameterMap.put(parameters[i], args[i]);
            }
        }
        log.info("acquire0 method:{} args:{} parameters:{}", targetMethod.getName(), args, Arrays.toString(parameters));

        RateLimit  limit = targetMethod.getAnnotation(RateLimit.class);
        if(limit==null){
            return;
        }
        String name = limit.limiterKey();
        if (StringUtils.isBlank(name)) {
            log.error("rateLimiter name is blank,skip");
            return;
        }
        //参数替换可优化
        String realName = name;
        for (Map.Entry<String, Object> entry : parameterMap.entrySet()) {
            String pattern = "#{" + entry.getKey() + "}";
            if (name.contains(pattern)) {
                Object val = entry.getValue();
                if (val == null) {
                    val = "null";
                }
                realName = realName.replace(pattern, val.toString());
            }
        }
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(realName);
        rateLimiter.trySetRate(RateType.OVERALL, limit.rate(), limit.rateInterval(), RateIntervalUnit.SECONDS);
        if(!rateLimiter.tryAcquire(1)){
            throw new RuntimeException("调用频率超限制，请稍后再试");
        }

    }

}
